use crate::{
    cli::Config,
    node::{Node, NodeType, Param},
    Value,
};

pub struct CallerParams {
    params: Vec<Value>,
}

impl From<Vec<Value>> for CallerParams {
    fn from(params: Vec<Value>) -> Self {
        Self { params }
    }
}

impl std::ops::Deref for CallerParams {
    type Target = Vec<Value>;

    fn deref(&self) -> &Self::Target {
        &self.params
    }
}

impl CallerParams {
    pub fn get_valid(&self, n: usize) -> Option<&Value> {
        if let Some(v) = self.params.get(n) {
            if v.is_valid() {
                Some(v)
            } else {
                None
            }
        } else {
            None
        }
    }

    pub fn display(&self, n: usize) -> Option<String> {
        if let Some(v) = self.get_valid(n) {
            Some(v.display())
        } else {
            None
        }
    }
}

/// 节点信息
#[allow(dead_code)]
pub struct RunningInfo<'a> {
    pub node: &'a Node,
    pub caller: &'a str,
    pub cli: &'a mut Config,
    pub nodelist: &'a NodeList,
    pub caller_params: CallerParams,
    pub token_params: &'a Vec<Param>,
}

impl RunningInfo<'_> {
    pub fn line(&self) -> String {
        format!("at line {}", crate::color!(red => self.node.line + 1))
    }

    pub fn line_len(&self) -> usize {
        format!("at line {}", self.node.line + 1).len()
    }
}

#[derive(Debug, Default)]
pub struct Ctrl {
    pub ptr: usize,
    pub work_dir: String,
    pub execlist: Vec<bool>,
    pub looplist: Vec<(usize, usize, usize)>,
    pub vars: Vec<std::collections::HashMap<String, Value>>,
}

impl Ctrl {
    pub fn init(len: usize) -> Self {
        macro_rules! make {
            ($e:expr) => {
                (0..len).into_iter().map(|_| $e).collect()
            };
        }

        let ptr = 0;
        let execlist = make!(true);
        let looplist = make!((0, 0, 0));
        let vars = make!(std::collections::HashMap::new());

        Self {
            ptr,
            vars,
            execlist,
            looplist,
            ..Ctrl::default()
        }
    }

    pub fn set<K, V>(&mut self, key: K, val: V, info: &RunningInfo)
    where
        K: ToString,
        V: Into<Value> + Clone,
    {
        let line = info.node.line;
        self.vars[line].insert(key.to_string(), val.clone().into());
        self.vars[info.nodelist.parents[line]].insert(key.to_string(), val.into());
    }

    pub fn set_scope<K, V>(&mut self, key: K, val: V, info: &RunningInfo)
    where
        K: ToString,
        V: Into<Value> + Clone,
    {
        let line = info.node.line;
        self.vars[line].insert(key.to_string(), val.clone().into());
    }

    pub fn seek(&self, param: &Param, info: &RunningInfo) -> Value {
        info.nodelist.seek(info.node.line, param, &self.vars)
    }
}

/// 代码节点树
#[derive(Default)]
pub struct NodeList {
    pub nodes: Vec<Node>,
    pub depth: Vec<usize>,
    pub parents: Vec<usize>,
    pub children: Vec<Vec<usize>>,
    pub callers: std::collections::HashMap<String, Box<dyn Fn(RunningInfo, &mut Ctrl)>>,
}

impl From<Vec<Node>> for NodeList {
    fn from(nodes: Vec<Node>) -> Self {
        NodeList {
            nodes,
            ..NodeList::default()
        }
    }
}

impl NodeList {
    fn list<T: Clone>(&self, v: T) -> Vec<T> {
        self.nodes.iter().map(|_| v.clone()).collect()
    }

    /// get the parents and children nodes
    pub fn init(&mut self) {
        let mut depth = 0;
        let mut scope = false;
        let mut stack = vec![];
        let mut depth_list = self.list(0);
        let mut parents_list = self.list(0);
        let mut children_list = self.list(vec![]);

        for node in self.nodes.iter_mut() {
            match &node.node_type {
                NodeType::Main => {
                    stack.push(node.line);
                    parents_list.push(0);
                }
                NodeType::Starter => {
                    if 0 == stack.len() {
                        crate::error!(
                            "Parsing scripts node failed at line {}",
                            crate::color!(red => node.line + 1)
                        );
                        crate::debug!(
                            "The grammer type of this node is {}",
                            crate::color!(cyan bold => "NodeType::Starter")
                        );

                        crate::exit!("Fatal Crash in {}", crate::color!(cyan => "NodeList::init"));
                    }

                    scope = false;
                    depth_list[node.line] = depth;
                    parents_list[node.line] = *stack.last().unwrap_or(&0);
                    depth = depth + 1;
                }
                NodeType::End => {
                    stack.pop();
                    depth = depth - 1;
                    depth_list[node.line] = depth;
                    parents_list[node.line] = *stack.last().unwrap_or(&0);
                }
                NodeType::Executor(_) => {
                    if true == scope {
                        stack.pop();
                    }

                    scope = true;

                    if 0 == stack.len() {
                        crate::error!(
                            "Parsing scripts node failed at line {}",
                            crate::color!(red => node.line + 1)
                        );
                        crate::debug!(
                            "The grammer type of this node is {}",
                            crate::color!(cyan bold => "NodeType::Executor")
                        );

                        crate::exit!("Fatal Crash in {}", crate::color!(cyan => "NodeList::init"));
                    }
                    parents_list[node.line] = *stack.last().unwrap();
                    depth_list[node.line] = depth;
                    stack.push(node.line);
                }
                _ => {}
            }
        }

        self.depth = depth_list;
        self.parents = parents_list;

        for node in &self.nodes {
            let line = node.line;
            let parent = self.parents[line];
            children_list[parent].push(line);
        }

        self.children = children_list;
    }

    fn seek(
        &self,
        line: usize,
        param: &Param,
        vars: &Vec<std::collections::HashMap<String, Value>>,
    ) -> Value {
        match param {
            Param::Number(value) => {
                let int = value.to_string().parse::<i64>();
                let float = value.to_string().parse::<f64>();

                match (int, float) {
                    (Ok(n), _) => n.into(),
                    (_, Ok(f)) => f.into(),
                    _ => param.into(),
                }
            }

            Param::Var(value) => {
                let mut i = line;
                loop {
                    if let Some(value) = vars[i].get(value) {
                        return value.clone();
                    }

                    // find upwards
                    i = self.parents[i];

                    // do the last checking at @Main
                    if i == 0 {
                        if let Some(value) = vars[i].get(value) {
                            return value.clone();
                        } else {
                            return param.into();
                        }
                    }
                }
            }

            Param::Static(value) => value.as_str().into(),
            _ => param.into(),
        }
    }

    pub fn run(&mut self, cli: &mut Config) {
        let mut ctrl = Ctrl::init(self.nodes.len());

        crate::work::prepare(cli, &mut ctrl);

        // list loop
        while ctrl.ptr < self.nodes.len() {
            let node = &self.nodes[ctrl.ptr];

            if false == ctrl.execlist[node.line] {
                // next point
                ctrl.ptr += 1;
                continue;
            }

            match &node.node_type {
                NodeType::Executor(e) => {
                    let caller = e.caller.as_str();
                    let caller_params = e
                        .params
                        .iter()
                        .map(|p| self.seek(node.line, p, &ctrl.vars))
                        .collect::<Vec<Value>>()
                        .into();

                    match self.callers.get(caller) {
                        Some(call) => {
                            call(
                                RunningInfo {
                                    cli,
                                    caller,
                                    node: &node,
                                    caller_params,
                                    nodelist: &self,
                                    token_params: &e.params,
                                },
                                &mut ctrl,
                            );
                        }
                        _ => {
                            crate::error!(
                                "Unexpected caller {} at line {}",
                                crate::color!(cyan => caller),
                                crate::color!(red => node.line + 1)
                            );
                            cli.may_exit();
                        }
                    }
                }

                NodeType::End => {
                    match ctrl.looplist[node.line] {
                        // if times == index then stop looping
                        (times, index, line) if times > index + 1 && line > 0 => {
                            ctrl.ptr = line;
                            continue;
                        }
                        _ => {}
                    }
                }
                _ => {}
            }

            ctrl.ptr += 1;
        }
    }

    pub fn provide<S, F>(&mut self, name: S, caller: F)
    where
        S: Into<String>,
        F: Fn(RunningInfo, &mut Ctrl) + 'static,
    {
        self.callers.insert(name.into(), Box::new(caller));
    }
}
